// Excel.driver.test.cpp : Defines the entry point for the console application.
//


#include <windows.h>
#include <ole2.h>
#include <oleacc.h>
#include <stdio.h>
#include <mshtml.h>

#define IDLE_TIMEOUT 120000L


#if !defined NTSTATUS
typedef LONG NTSTATUS;
#endif

#if !defined PROCESSINFOCLASS
typedef LONG PROCESSINFOCLASS;
#endif

#if !defined PPEB
typedef struct _PEB *PPEB;
#endif

#if !defined PROCESS_BASIC_INFORMATION
typedef struct _PROCESS_BASIC_INFORMATION {
    PVOID Reserved1;
    PPEB PebBaseAddress;
    PVOID Reserved2[2];
    ULONG_PTR UniqueProcessId;
    PVOID Reserved3;
} PROCESS_BASIC_INFORMATION;
#endif

typedef NTSTATUS (WINAPI * PFN_ZWQUERYINFORMATIONPROCESS)(HANDLE, PROCESSINFOCLASS,
    PVOID, ULONG, PULONG);


DWORD GetProcessIDbyProcessHandleZw(HANDLE hProcess)
{
    HINSTANCE hNtDll = LoadLibraryW(L"ntdll.dll");
    if (hNtDll == NULL)    return 0xffffffff;
    PFN_ZWQUERYINFORMATIONPROCESS fnProcInfo = PFN_ZWQUERYINFORMATIONPROCESS(
        ::GetProcAddress(hNtDll, "ZwQueryInformationProcess"));
    if (fnProcInfo == NULL)
    {
        printf("ZwQueryInformationProcess function not found\n");
        CloseHandle(hNtDll);
        return 0xffffffff;
    }
    PROCESS_BASIC_INFORMATION pbi;
    ZeroMemory(&pbi, sizeof(PROCESS_BASIC_INFORMATION));
    if (fnProcInfo(hProcess, 0, &pbi, sizeof(PROCESS_BASIC_INFORMATION), NULL) == 0)
    {
        CloseHandle(hNtDll);
        return pbi.UniqueProcessId;
    }
    else
    {
    	printf("ZwQueryInformationProcess function didn't work\n");

        CloseHandle(hNtDll);
        return 0xffffffff;
    }
}

#define MAX_LAZY 32768

IDispatch * lazy_release_queue[MAX_LAZY + 1];
int lazy_pos = 0;

void LazyRelease(IDispatch * pd)
{
  if (lazy_pos < MAX_LAZY)
  {
	 lazy_release_queue[lazy_pos++] = pd;
  }
  else pd->Release();
}

unsigned long TimeStampMillis()
{
  SYSTEMTIME st;
  FILETIME ft;
  ULARGE_INTEGER ul;
  GetSystemTime(&st);
  SystemTimeToFileTime(&st, &ft);
  ul.u.HighPart = ft.dwHighDateTime;
  ul.u.LowPart = ft.dwLowDateTime;
  return (unsigned long) (ul.QuadPart / 10000);
}

void LazySleep(long sleep_length)
{
   unsigned long start = TimeStampMillis();
   unsigned long elapsed = 0;
   while ((lazy_pos > 0) && (elapsed < sleep_length))
   {
	  lazy_release_queue[--lazy_pos]->Release();
	  elapsed = TimeStampMillis() - start;
    }
   if (elapsed < sleep_length)
   {
	  long sleep_time = (long) (sleep_length - elapsed);
      Sleep(sleep_time);
   }
}

int initialised = 0;

  HWND FindIEControl(HWND w)
  {
    HWND b, r;
    char classbuffer[256];
    short id;

    // Check top level control
    id = GetDlgCtrlID(w);
    GetClassName(w, classbuffer, 256);
    if (strcmp(classbuffer,"Internet Explorer_Server") == 0)
    {
        return w;
    }

    b = FindWindowEx(w, NULL, NULL, NULL);
    while(b)
    {
      id = GetDlgCtrlID(b);
      GetClassName(b, classbuffer, 256);
      if (strcmp(classbuffer,"Internet Explorer_Server") == 0)
      {
         return b;
      }
      r = FindIEControl(b);
      if (r) return r;
      b = FindWindowEx(w, b, NULL, NULL);
    }
    return 0;
  }

HWND FindControl(HWND w, char * text)
  {
    HWND b, r;
    char buffer[256];
    char classbuffer[256];
    short id;

    b = FindWindowEx(w, NULL, NULL, NULL);
    while(b)
    {
      SendMessage(b, WM_GETTEXT, 256, (int) buffer);
      id = GetDlgCtrlID(b);
      GetClassName(b, classbuffer, 256);

      if (strcmp(buffer, text) == 0)
      {
         return b;
      }
      r = FindControl(b, text);
      if (r) return r;
      b = FindWindowEx(w, b, NULL, NULL);
    }
    return 0;
  }

HWND FindEditControl(HWND w, int pos)
  {
    HWND b, r;
    char buffer[256];
    char classbuffer[256];
    short id;
    int p = pos;

    b = FindWindowEx(w, NULL, NULL, NULL);
    while(b)
    {
      id = GetDlgCtrlID(b);
      GetClassName(b, classbuffer, 256);

      if (strcmp(classbuffer, "Edit") == 0)
      {
         p--;
         if (p == 0) return b;
      }
      r = FindEditControl(b, p);
      if (r) return r;
      b = FindWindowEx(w, b, NULL, NULL);
    }
    return 0;
  }


extern "C" int find_window(WCHAR * name)
{
   HWND w = 0;
   BSTR unicodestr = 0;
   unicodestr = SysAllocString(name);
   int tries = 0;

   // Convert String
   int a = SysStringLen(unicodestr)+1;
   char *ansistr = new char[a];
   WideCharToMultiByte(CP_ACP,  0, unicodestr, -1, ansistr, a, NULL, NULL);

   while ((!w) && (tries < 50))
   {
      w = FindWindow(NULL, ansistr);
      if (!w) LazySleep(250);
      tries++;
   }

   delete[] ansistr;
   SysFreeString(unicodestr);

   return (int) w;
}

void WaitForWindow(HWND w)
{
   DWORD pid;
   HANDLE hProcess;
   GetWindowThreadProcessId(w, &pid);
   hProcess = OpenProcess(1040, 0, pid);
   WaitForInputIdle(hProcess, IDLE_TIMEOUT);
   CloseHandle(hProcess);
}

IHTMLDocument2 * get_IHTMLDocument2(HWND w)
{
  HWND ie = 0;

   if (!initialised)
   {
     OleInitialize(NULL);
     initialised = 1;
   }

  // Make sure process is now idle
   WaitForWindow(w);

  if (w)
  {
    ie = FindIEControl(w);
    if (ie)
    {
      // Found IE Control
      IDispatch * pDisp = 0;
      LRESULT lRes;
      IHTMLDocument2 * spDoc;

			UINT nMsg = RegisterWindowMessage( TEXT("WM_HTML_GETOBJECT") );
			SendMessageTimeout(ie, nMsg, 0L, 0L, SMTO_ABORTIFHUNG, 1000, (DWORD*)&lRes );

      HRESULT hr;
			hr = ObjectFromLresult( lRes, IID_IHTMLDocument2, 0, (void**)&spDoc );
			if ( SUCCEEDED(hr) )
			{
				// Change background color to red
        return spDoc;
			}
      else
      {
          printf("find_browser_window: ObjectFromLResult failed: facility=%lx, severity=%lx, code=%lx\n", HRESULT_FACILITY(hr), HRESULT_SEVERITY(hr),HRESULT_CODE(hr));
          return 0;
      }

    }
    else
    {
        return 0;
    }
  }
  return 0;
}

extern "C" int new_window(char * path)
{
  SHELLEXECUTEINFO sei = { sizeof(sei) };
  sei.fMask = SEE_MASK_FLAG_DDEWAIT | SEE_MASK_NOCLOSEPROCESS;
  sei.nShow = SW_SHOWNORMAL;
  sei.lpVerb = "open";
  sei.lpFile = path;

  char classbuffer[256];
  HWND h, hwnd, parent;

  if (ShellExecuteEx(&sei))
  {
	 LazySleep(500);
     DWORD id = GetProcessIDbyProcessHandleZw(sei.hProcess);
     h = GetTopWindow(0 );
     while ( h )
     {
       DWORD pid;
       DWORD dwTheardId = GetWindowThreadProcessId( h,&pid);
         if (( pid == id ) && (GetParent(h) == 0))
         {
            // here h is the handle to the window
            break;
         }
         h = GetNextWindow( h , GW_HWNDNEXT);
     }

  }
  else return 0;
  return (int) h;
}

extern "C" int com_create_object(WCHAR * name)
{
   CLSID clsid;
   HRESULT hr;
   IDispatch *pDisp;

   if (!initialised)
   {
     OleInitialize(NULL);
     initialised = 1;
   }

   hr = CLSIDFromProgID(name, &clsid);
   hr = CoCreateInstance(clsid, NULL, CLSCTX_LOCAL_SERVER, IID_IDispatch, (void **)&pDisp);

   if (FAILED(hr))
   {
     printf("createinstance failed: facility=%lx, severity=%lx, code=%ld\n",
     HRESULT_FACILITY(hr), HRESULT_SEVERITY(hr),
     HRESULT_CODE(hr));
    }

    return (int) pDisp;
}

extern "C" void com_set_property(int o, WCHAR * name, int value)
{
  HRESULT hr;
  IDispatch *pDisp = (IDispatch *) o;
  ITypeInfo *pTInfo;
  TYPEATTR * typeAttr;
  VARDESC * varDesc;
  FUNCDESC * funcDesc;
  BSTR varName;
  unsigned int dummy = 1;

  DISPID dispID;
  MEMBERID memID;
  DISPID dispidNamed = DISPID_PROPERTYPUT;
  DISPPARAMS dispParams = { NULL, NULL, 0, 0 };
  VARIANT parm1;

  pDisp->GetIDsOfNames(IID_NULL, &name, 1, LOCALE_USER_DEFAULT, &dispID);

  /* Find type */
  hr = pDisp->GetTypeInfo(0, LOCALE_USER_DEFAULT, &pTInfo);
  hr = pTInfo->GetTypeAttr(&typeAttr);
  printf("Functions: %d\n", typeAttr->cFuncs);
  printf("Variables: %d\n", typeAttr->cVars);


  if FAILED(hr)
  {
    printf("com_set_property GetTypeInfo failed: facility=%lx, severity=%lx, code=%ld\n",
    HRESULT_FACILITY(hr), HRESULT_SEVERITY(hr),
    HRESULT_CODE(hr));
  }


  VariantInit(&parm1);
  V_VT(&parm1) = VT_BOOL;
  V_BOOL(&parm1) = TRUE;

  /* One argument. */
  dispParams.cArgs = 1;
  dispParams.rgvarg = &parm1;

  /* Handle special-case for property-puts! */
  dispParams.cNamedArgs = 1;
  dispParams.rgdispidNamedArgs = &dispidNamed;

  hr = pDisp->Invoke(dispID, IID_NULL, LOCALE_SYSTEM_DEFAULT, DISPATCH_PROPERTYPUT | DISPATCH_METHOD, &dispParams, NULL, NULL, NULL);

  if FAILED(hr)
  {
    printf("setprop failed: facility=%lx, severity=%lx, code=%ld\n",
    HRESULT_FACILITY(hr), HRESULT_SEVERITY(hr),
    HRESULT_CODE(hr));
  }

}


HRESULT countArgsInFormat(LPCTSTR pszFmt, UINT FAR *pn)
 {
     *pn = 0;
     if(pszFmt == NULL)
       return NOERROR;

     while (*pszFmt)
     {
        if (*pszFmt == '&')
            pszFmt++;

        switch(*pszFmt)
        {
            case 'b':
            case 'i':
            case 'I':
            case 'r':
            case 'R':
            case 'c':
            case 's':
            case 'e':
            case 'd':
            case 'v':
            case 'D':
            case 'U':
                ++*pn;
                pszFmt++;
                break;
            case '\0':
            default:
                return ResultFromScode(E_INVALIDARG);
         }
     }
     return NOERROR;
 }

LPCTSTR getNextVarType(LPCTSTR pszFmt, VARTYPE FAR* pvt)
 {
     *pvt = 0;
     if (*pszFmt == '&')
     {
          *pvt = VT_BYREF;
          pszFmt++;
          if (!*pszFmt)
              return NULL;
     }
     switch(*pszFmt)
     {
         case 'b':
             *pvt |= VT_BOOL;
             break;
         case 'i':
             *pvt |= VT_I2;
             break;
         case 'I':
             *pvt |= VT_I4;
             break;
         case 'r':
             *pvt |= VT_R4;
             break;
         case 'R':
             *pvt |= VT_R8;
             break;
         case 'c':
             *pvt |= VT_CY;
             break;
         case 's':
             *pvt |= VT_BSTR;
             break;
         case 'e':
             *pvt |= VT_ERROR;
             break;
         case 'd':
             *pvt |= VT_DATE;
             break;
         case 'v':
             *pvt |= VT_VARIANT;
             break;
         case 'U':
             *pvt |= VT_UNKNOWN;
             break;
         case 'D':
             *pvt |= VT_DISPATCH;
             break;
         case '\0':
              return NULL;     // End of Format string
         default:
             return NULL;
     }
     return ++pszFmt;
 }


 /*
00053  * Invoke
00054  *
00055  * Purpose:
00056  *  Invokes a property accessor function or method of an automation object. Uses Unicode with OLE.
00057  *
00058  * Parameters:
00059  *  pdisp         IDispatch* of automation object.
00060  *  wFlags        Specfies if property is to be accessed or method to be invoked.
00061  *                Can hold DISPATCH_PROPERTYGET, DISPATCH_PROPERTYPUT, DISPATCH_METHOD,
00062  *                DISPATCH_PROPERTYPUTREF or DISPATCH_PROPERTYGET|DISPATCH_METHOD.
00063  *  pvRet         NULL if caller excepts no result. Otherwise returns result.
00064  *  pexcepinfo    Returns exception info if DISP_E_EXCEPTION is returned. Can be NULL if
00065  *                caller is not interested in exception information.
00066  *  pnArgErr      If return is DISP_E_TYPEMISMATCH, this returns the index (in reverse
00067  *                order) of argument with incorrect type. Can be NULL if caller is not interested
00068  *                in this information.
00069  *  pszName       Name of property or method.
00070  *  pszFmt        Format string that describes the variable list of parameters that
00071  *                follows. The format string can contain the follwoing characters.
00072  *                & = mark the following format character as VT_BYREF
00073  *                b = VT_BOOL
00074  *                i = VT_I2
00075  *                I = VT_I4
00076  *                r = VT_R2
00077  *                R = VT_R4
00078  *                c = VT_CY
00079  *                s = VT_BSTR (far string pointer can be passed, BSTR will be allocated by this function).
00080  *                e = VT_ERROR
00081  *                d = VT_DATE
00082  *                v = VT_VARIANT. Use this to pass data types that are not described in
00083  *                                the format string. (For example SafeArrays).
00084  *                D = VT_DISPATCH
00085  *                U = VT_UNKNOWN
00086  *
00087  *  ...           Arguments of the property or method. Arguments are described by pszFmt.
00088  *                ****FAR POINTERS MUST BE PASSED FOR POINTER ARGUMENTS in Win16.****
00089  * Return Value:
00090  *  HRESULT indicating success or failure
00091  *
00092  * Usage examples:
00093  *
00094  *  HRESULT hr;
00095  *  LPDISPATCH pdisp;
00096  *  BSTR bstr;
00097  *  short i;
00098  *  BOOL b;
00099  *  VARIANT v, v2;
00100  *
00101  *1. bstr = SysAllocString(OLESTR(""));
00102  *   hr = Invoke(pdisp, DISPATCH_METHOD, NULL, NULL, NULL, OLESTR("method1"),
00103  *        TEXT("bis&b&i&s"), TRUE, 2, (LPOLESTR)OLESTR("param"), (BOOL FAR*)&b, (short FAR*)&i, (BSTR FAR*)&bstr);
00104  *
00105  *2. VariantInit(&v);
00106  *   V_VT(&v) = VT_R8;
00107  *   V_R8(&v) = 12345.6789;
00108  *   VariantInit(&v2);
00109  *   hr = Invoke(pdisp, DISPATCH_METHOD, NULL, NULL, NULL, OLESTR("method2"),
00110  *         TEXT("v&v"), v, (VARIANT FAR*)&v2);
00111  */

HRESULT ole_invoke(LPDISPATCH pdisp,
     WORD wFlags,
     LPVARIANT pvRet,
     EXCEPINFO FAR* pexcepinfo,
     UINT FAR* pnArgErr,
     LPOLESTR pszName,
     LPCTSTR pszFmt,
     ...)
{
     va_list argList;
     va_start(argList, pszFmt);
     DISPID dispid;
     HRESULT hr;
     VARIANTARG* pvarg = NULL;


     if (pdisp == NULL)
         return ResultFromScode(E_INVALIDARG);

     // Get DISPID of property/method
     hr = pdisp->GetIDsOfNames(IID_NULL, &pszName, 1, LOCALE_USER_DEFAULT, &dispid);
     if(FAILED(hr))
         return hr;

     DISPPARAMS dispparams;
     memset(&dispparams, 0, sizeof dispparams);

     // determine number of arguments
     if (pszFmt != NULL)
         countArgsInFormat(pszFmt, &dispparams.cArgs);

     // Property puts have a named argument that represents the value that the property is
     // being assigned.
     DISPID dispidNamed = DISPID_PROPERTYPUT;
     if (wFlags & DISPATCH_PROPERTYPUT)
     {
         if (dispparams.cArgs == 0)
             return ResultFromScode(E_INVALIDARG);
         dispparams.cNamedArgs = 1;
         dispparams.rgdispidNamedArgs = &dispidNamed;
     }

     if (dispparams.cArgs != 0)
     {
         // allocate memory for all VARIANTARG parameters
         pvarg = new VARIANTARG[dispparams.cArgs];
         if(pvarg == NULL)
             return ResultFromScode(E_OUTOFMEMORY);
         dispparams.rgvarg = pvarg;
         memset(pvarg, 0, sizeof(VARIANTARG) * dispparams.cArgs);

         // get ready to walk vararg list
         LPCTSTR psz = pszFmt;
         pvarg += dispparams.cArgs - 1;   // params go in opposite order

         while (psz = getNextVarType(psz, &pvarg->vt))
         {
             if (pvarg < dispparams.rgvarg)
             {
                 hr = ResultFromScode(E_INVALIDARG);
                 goto cleanup;
             }
             switch (pvarg->vt)
             {
             case VT_I2:
                 V_I2(pvarg) = va_arg(argList, short);
                 break;
             case VT_I4:
                 V_I4(pvarg) = va_arg(argList, long);
                 break;
             case VT_R4:
                 V_R4(pvarg) = va_arg(argList, float);
                 break;
             case VT_DATE:
             case VT_R8:
                 V_R8(pvarg) = va_arg(argList, double);
                 break;
             case VT_CY:
                 V_CY(pvarg) = va_arg(argList, CY);
                 break;
             case VT_BSTR:
                 V_BSTR(pvarg) = SysAllocString(va_arg(argList, OLECHAR FAR*));
                 if (pvarg->bstrVal == NULL)
                 {
                     hr = ResultFromScode(E_OUTOFMEMORY);
                     pvarg->vt = VT_EMPTY;
                     goto cleanup;
                 }
                 break;
             case VT_DISPATCH:
                 V_DISPATCH(pvarg) = va_arg(argList, LPDISPATCH);
                 break;
             case VT_ERROR:
                 V_ERROR(pvarg) = va_arg(argList, SCODE);
                 break;
             case VT_BOOL:
                 V_BOOL(pvarg) = va_arg(argList, BOOL) ? -1 : 0;
                 break;
             case VT_VARIANT:
                 *pvarg = va_arg(argList, VARIANTARG);
                 break;
             case VT_UNKNOWN:
                 V_UNKNOWN(pvarg) = va_arg(argList, LPUNKNOWN);
                 break;

             case VT_I2|VT_BYREF:
                 V_I2REF(pvarg) = va_arg(argList, short FAR*);
                 break;
             case VT_I4|VT_BYREF:
                 V_I4REF(pvarg) = va_arg(argList, long FAR*);
                 break;
             case VT_R4|VT_BYREF:
                 V_R4REF(pvarg) = va_arg(argList, float FAR*);
                 break;
             case VT_R8|VT_BYREF:
                 V_R8REF(pvarg) = va_arg(argList, double FAR*);
                 break;
             case VT_DATE|VT_BYREF:
                 V_DATEREF(pvarg) = va_arg(argList, DATE FAR*);
                 break;
             case VT_CY|VT_BYREF:
                 V_CYREF(pvarg) = va_arg(argList, CY FAR*);
                 break;
             case VT_BSTR|VT_BYREF:
                 V_BSTRREF(pvarg) = va_arg(argList, BSTR FAR*);
                 break;
             case VT_DISPATCH|VT_BYREF:
                 V_DISPATCHREF(pvarg) = va_arg(argList, LPDISPATCH FAR*);
                 break;
             case VT_ERROR|VT_BYREF:
                 V_ERRORREF(pvarg) = va_arg(argList, SCODE FAR*);
                 break;
             case VT_BOOL|VT_BYREF:
                 {
                     BOOL FAR* pbool = va_arg(argList, BOOL FAR*);
                     *pbool = 0;
                     V_BOOLREF(pvarg) = (VARIANT_BOOL FAR*)pbool;
                 }
                 break;
             case VT_VARIANT|VT_BYREF:
                 V_VARIANTREF(pvarg) = va_arg(argList, VARIANTARG FAR*);
                 break;
             case VT_UNKNOWN|VT_BYREF:
                 V_UNKNOWNREF(pvarg) = va_arg(argList, LPUNKNOWN FAR*);
                 break;

             default:
                 {
                     hr = ResultFromScode(E_INVALIDARG);
                     goto cleanup;
                 }
                 break;
             }

             --pvarg; // get ready to fill next argument
         } //while
     } //if

     // Initialize return variant, in case caller forgot. Caller can pass NULL if return
     // value is not expected.
     if (pvRet)
         VariantInit(pvRet);
     // make the call


         try {
                 hr = pdisp->Invoke(dispid, IID_NULL, LOCALE_USER_DEFAULT, wFlags,
                         &dispparams, pvRet, pexcepinfo, pnArgErr);
         }
         catch (...) {
                 hr = E_FAIL;
         }


 cleanup:
     // cleanup any arguments that need cleanup
     if (dispparams.cArgs != 0)
     {
         VARIANTARG FAR* pvarg = dispparams.rgvarg;
         UINT cArgs = dispparams.cArgs;

         while (cArgs--)
         {
             switch (pvarg->vt)
             {
             case VT_BSTR:
                 VariantClear(pvarg);
                 break;
             }
             ++pvarg;
         }
     }
 //    delete dispparams.rgvarg;
     va_end(argList);
     return hr;
 }


extern "C" void com_invoke(int o, wchar_t * name, wchar_t * value)
 {
   IDispatch *pDisp = (IDispatch *) o;
   ole_invoke(pDisp, DISPATCH_METHOD, NULL, NULL, NULL, name, TEXT("s"), (OLECHAR FAR*) value);
 }

extern "C" int browser_create(wchar_t * url)
 {
   HRESULT hr;
   VARIANT w;
   VariantInit(&w);
   HWND hwnd = 0;
   IDispatch *pDisp = (IDispatch *) com_create_object(L"InternetExplorer.Application");
   ole_invoke(pDisp, DISPATCH_PROPERTYPUT, NULL, NULL, NULL, L"Visible", TEXT("b"), TRUE);
   ole_invoke(pDisp, DISPATCH_METHOD, NULL, NULL, NULL, L"Navigate", TEXT("s"), (OLECHAR FAR*) url);
   hr = ole_invoke(pDisp, DISPATCH_PROPERTYGET, &w, NULL, NULL, L"HWND", NULL);
   if FAILED(hr) printf("browser_create: HWND: facility=%lx, severity=%lx, code=%lx\n", HRESULT_FACILITY(hr), HRESULT_SEVERITY(hr),HRESULT_CODE(hr));
   hwnd = (HWND) w.lVal;
   VariantClear(&w);

  // Wait to stop loading
  BOOLEAN busy = TRUE;
  VariantInit(&w);

  while (busy)
  {
     HRESULT hr = ole_invoke(pDisp, DISPATCH_PROPERTYGET, &w, NULL, NULL, L"Busy", NULL);
     if FAILED(hr)
     {
         printf("browser_create: Busy: facility=%lx, severity=%lx, code=%lx\n", HRESULT_FACILITY(hr), HRESULT_SEVERITY(hr),HRESULT_CODE(hr));
         return 0;
     }
     busy = w.boolVal;
     if (busy) LazySleep(250);
   }
   VariantClear(&w);

   return (int) hwnd;
 }

 int browser_wait_not_busy(IHTMLDocument2 * doc)
 {
   BOOLEAN busy = TRUE;
   BSTR rs;
   while (busy)
   {
     HRESULT hr = doc->get_readyState(&rs);
     if FAILED(hr)
     {
         printf("browser_wait_not_busy: Busy: facility=%lx, severity=%lx, code=%lx\n", HRESULT_FACILITY(hr), HRESULT_SEVERITY(hr),HRESULT_CODE(hr));
         return -1;
     }
     if (wcscmp(rs, L"complete") ==0) busy = FALSE;
     SysFreeString(rs);
     if (busy) LazySleep(250);
   }
 }


extern "C" void browser_navigate_to(HWND o, wchar_t * url)
 {
     HWND ie = (HWND) o;
     IHTMLDocument2 * doc = get_IHTMLDocument2(ie);

     if (doc)
    {
       BSTR text = SysAllocString(url);
       browser_wait_not_busy(doc);
       doc->put_URL(text);
       SysFreeString(text);
    }
 }


 extern "C" int browser_window_close (int o)
 {
     HWND ie = (HWND) o;
     IHTMLDocument2 * doc = get_IHTMLDocument2(ie);
     IHTMLWindow2 * win;

     if (doc)
    {
       doc->get_parentWindow(&win);
       win->close();
       win->Release();
       doc->Release();
    }
    else
    {
        WaitForWindow(ie);
        SendMessageTimeout(ie, WM_CLOSE, NULL, NULL, SMTO_NORMAL, 1, NULL);
    }
 }

 IHTMLElement * GetElementByTagAndText(IHTMLDocument2 * doc, wchar_t * tag, wchar_t * text)
 {
    HRESULT hr;
    VARIANT r, v1, v2;
    VariantInit(&v1);
    VariantInit(&v2);
    long length;
    long i = 0;
    int foundTag = 0;

    IHTMLElementCollection * coll;
    IHTMLElement * element = NULL;
    IDispatch * pd;

    hr = doc->get_all(&coll);
    if FAILED(hr)
    {
        printf("GetElementByTagAndText: get All: facility=%lx, severity=%lx, code=%lx\n", HRESULT_FACILITY(hr), HRESULT_SEVERITY(hr),HRESULT_CODE(hr));
        return NULL;
    }

    hr = coll->get_length(&length);
    if FAILED(hr)
    {
      printf("GetElementByTagAndText: get Length: facility=%lx, severity=%lx, code=%lx\n", HRESULT_FACILITY(hr), HRESULT_SEVERITY(hr),HRESULT_CODE(hr));
      return NULL;
    }

    while ((i < length) && (!foundTag))
    {
       foundTag = FALSE;

       VariantInit(&v1);
       v1.vt = VT_UINT;
       v1.lVal = i;
       hr = coll->item(v1, v2, &pd);
       if FAILED(hr) printf("GetElementByTagAndText: collection item: facility=%lx, severity=%lx, code=%lx\n", HRESULT_FACILITY(hr), HRESULT_SEVERITY(hr),HRESULT_CODE(hr));
       VariantClear(&v1);

       VariantInit(&r);
       hr = ole_invoke(pd, DISPATCH_PROPERTYGET, &r, NULL, NULL, L"tagName", NULL);
       if FAILED(hr) printf("GetElementByTagAndText: Get tagname: facility=%lx, severity=%lx, code=%lx\n", HRESULT_FACILITY(hr), HRESULT_SEVERITY(hr),HRESULT_CODE(hr));
       if (wcscmp(r.bstrVal, tag) ==0) foundTag = TRUE;
       SysFreeString(r.bstrVal);
       VariantClear(&r);

       if (foundTag)
       {
          VariantInit(&r);
          hr = ole_invoke(pd, DISPATCH_PROPERTYGET, &r, NULL, NULL, L"innerText", NULL);
          if FAILED(hr) printf("GetElementByTagAndText: Get tagname: facility=%lx, severity=%lx, code=%lx\n", HRESULT_FACILITY(hr), HRESULT_SEVERITY(hr),HRESULT_CODE(hr));
          if (r.bstrVal) {
            if (wcscmp(r.bstrVal, text) !=0) foundTag = FALSE;
            SysFreeString(r.bstrVal);
          }
          else foundTag = FALSE;
          VariantClear(&r);
        }
       if (!foundTag) LazyRelease(pd);
       i++;
    }

    if (foundTag)
    {
       hr = pd->QueryInterface(IID_IHTMLElement, (void **) &element);
       if FAILED(hr) printf("GetElementByTagAndText: Element query interface: facility=%lx, severity=%lx, code=%lx\n", HRESULT_FACILITY(hr), HRESULT_SEVERITY(hr),HRESULT_CODE(hr));
       pd->Release();
    }

    coll->Release();
    return element;
 }


 IHTMLElement * GetElementByTagAttrAndPos(IHTMLDocument2 * doc, wchar_t * tag, wchar_t * attr_name, wchar_t * attr_value, int pos)
 {
    HRESULT hr;
    VARIANT r, v1, v2;
    VariantInit(&v1);
    VariantInit(&v2);
    long length;
    long i = 0;
    int foundTag = 0;

    IHTMLElementCollection * coll;
    IHTMLElement * element = NULL;
    IDispatch * pd = NULL;
    BSTR tagName;

    hr = doc->get_all(&coll);
    if FAILED(hr)
    {
        printf("GetElementByTagAttrAndPos: get All: facility=%lx, severity=%lx, code=%lx\n", HRESULT_FACILITY(hr), HRESULT_SEVERITY(hr),HRESULT_CODE(hr));
        return NULL;
    }

    hr = coll->get_length(&length);
    if FAILED(hr)
    {
      printf("GetElementByTagAttrAndPos: get Length: facility=%lx, severity=%lx, code=%lx\n", HRESULT_FACILITY(hr), HRESULT_SEVERITY(hr),HRESULT_CODE(hr));
      return NULL;
    }

    while ((i < length) && (!foundTag))
    {
       foundTag = FALSE;

       VariantInit(&v1);
       v1.vt = VT_UINT;
       v1.lVal = i;
       if (pd) LazyRelease(pd);
       hr = coll->item(v1, v2, &pd);
       if FAILED(hr) printf("GetElementByTagAttrAndPos: collection item: facility=%lx, severity=%lx, code=%lx\n", HRESULT_FACILITY(hr), HRESULT_SEVERITY(hr),HRESULT_CODE(hr));
       VariantClear(&v1);

       hr = pd->QueryInterface(IID_IHTMLElement, (void **) &element);
       if FAILED(hr) printf("GetElementByTagAttrAndPos: Element query interface: facility=%lx, severity=%lx, code=%lx\n", HRESULT_FACILITY(hr), HRESULT_SEVERITY(hr),HRESULT_CODE(hr));

       hr = element->get_tagName(&tagName);
       if FAILED(hr) printf("GetElementByTagAttrAndPos: Get tagname: facility=%lx, severity=%lx, code=%lx\n", HRESULT_FACILITY(hr), HRESULT_SEVERITY(hr),HRESULT_CODE(hr));
       if (wcscmp(tagName, tag) ==0) foundTag = TRUE;
       SysFreeString(tagName);

       if (foundTag)
       {
    	  if (attr_name)
    	  {
             VariantInit(&r);
             hr = element->getAttribute(attr_name, 0, &r);
             if FAILED(hr) printf("GetElementByTagAttrAndPos: Get Attribute: facility=%lx, severity=%lx, code=%lx\n", HRESULT_FACILITY(hr), HRESULT_SEVERITY(hr),HRESULT_CODE(hr));
             if (wcsstr(attr_value, r.bstrVal) !=NULL)
             {
                pos--;
                if (pos != 0) foundTag = FALSE;
             }
             else foundTag = FALSE;
             SysFreeString(r.bstrVal);
             VariantClear(&r);
    	  }
    	  else
    	  {
             pos--;
             if (pos != 0) foundTag = FALSE;
    	  }
        }
       i++;
       element->Release();
    }

    if (foundTag)
    {
       hr = pd->QueryInterface(IID_IHTMLElement, (void **) &element);
       if FAILED(hr) printf("GetElementByTagAttrAndPos: Element query interface: facility=%lx, severity=%lx, code=%lx\n", HRESULT_FACILITY(hr), HRESULT_SEVERITY(hr),HRESULT_CODE(hr));
       pd->Release();
    }
    else element = NULL;

    coll->Release();
    return element;
 }

 IHTMLElement * GetElementByTagAttrAndAttr(IHTMLDocument2 * doc, wchar_t * tag, wchar_t * attr1_name, wchar_t * attr1_value, wchar_t * attr2_name, wchar_t * attr2_value)
 {
    HRESULT hr;
    VARIANT r1, r2, v1, v2;
    VariantInit(&v1);
    VariantInit(&v2);
    long length;
    long i = 0;
    int foundTag = 0;

    IHTMLElementCollection * coll;
    IHTMLElement * element = NULL;
    IDispatch * pd;
    BSTR tagName;

    hr = doc->get_all(&coll);
    if FAILED(hr)
    {
        printf("GetElementByTagAttrAndAttr: get All: facility=%lx, severity=%lx, code=%lx\n", HRESULT_FACILITY(hr), HRESULT_SEVERITY(hr),HRESULT_CODE(hr));
        return NULL;
    }

    hr = coll->get_length(&length);
    if FAILED(hr)
    {
      printf("GetElementByTagAttrAndAttr: get Length: facility=%lx, severity=%lx, code=%lx\n", HRESULT_FACILITY(hr), HRESULT_SEVERITY(hr),HRESULT_CODE(hr));
      return NULL;
    }

    while ((i < length) && (!foundTag))
    {
       foundTag = FALSE;

       VariantInit(&v1);
       v1.vt = VT_UINT;
       v1.lVal = i;
       hr = coll->item(v1, v2, &pd);
       if FAILED(hr) printf("GetElementByTagAttrAndAttr: collection item: facility=%lx, severity=%lx, code=%lx\n", HRESULT_FACILITY(hr), HRESULT_SEVERITY(hr),HRESULT_CODE(hr));
       VariantClear(&v1);

       hr = pd->QueryInterface(IID_IHTMLElement, (void **) &element);
       if FAILED(hr) printf("GetElementByTagAttrAndAttr: Element query interface: facility=%lx, severity=%lx, code=%lx\n", HRESULT_FACILITY(hr), HRESULT_SEVERITY(hr),HRESULT_CODE(hr));

       hr = element->get_tagName(&tagName);
       if FAILED(hr) printf("GetElementByTagAttrAndAttr: Get tagname: facility=%lx, severity=%lx, code=%lx\n", HRESULT_FACILITY(hr), HRESULT_SEVERITY(hr),HRESULT_CODE(hr));
       if (wcscmp(tagName, tag)==0) foundTag = TRUE;
       SysFreeString(tagName);

       if (foundTag)
       {
          VariantInit(&r1);
          hr = element->getAttribute(attr1_name, 0, &r1);
          if FAILED(hr) printf("GetElementByTagAttrAndAttr: Get Attribute 1: facility=%lx, severity=%lx, code=%lx\n", HRESULT_FACILITY(hr), HRESULT_SEVERITY(hr),HRESULT_CODE(hr));
          if (wcsstr(attr1_value, r1.bstrVal) !=NULL)
          {
             if (attr2_name)
             {
                VariantInit(&r2);
                hr = element->getAttribute(attr2_name, 0, &r2);
                if FAILED(hr) printf("GetElementByTagAttrAndAttr: Get Attribute 2: facility=%lx, severity=%lx, code=%lx\n", HRESULT_FACILITY(hr), HRESULT_SEVERITY(hr),HRESULT_CODE(hr));
                if (wcsstr(attr2_value, r2.bstrVal) ==NULL)
               {
                  foundTag = FALSE;
               }
               SysFreeString(r2.bstrVal);
               VariantClear(&r2);
             }
          }
          else foundTag = FALSE;
          SysFreeString(r1.bstrVal);
          VariantClear(&r1);
        }
       if (!foundTag) LazyRelease(pd);
       i++;
       element->Release();
    }

    if (foundTag)
    {
       hr = pd->QueryInterface(IID_IHTMLElement, (void **) &element);
       if FAILED(hr) printf("GetElementByTagAttrAndAttr: Element query interface: facility=%lx, severity=%lx, code=%lx\n", HRESULT_FACILITY(hr), HRESULT_SEVERITY(hr),HRESULT_CODE(hr));
       pd->Release();
    }
    else element = NULL;
    coll->Release();
    return element;
 }


 IHTMLElement * GetChildElementByTagAttrAndAttr(IHTMLElement * doc, wchar_t * tag, wchar_t * attr1_name, wchar_t * attr1_value, wchar_t * attr2_name, wchar_t * attr2_value)
  {
     HRESULT hr;
     VARIANT r1, r2, v1, v2;
     VariantInit(&v1);
     VariantInit(&v2);
     long length;
     long i = 0;
     int foundTag = 0;

     IHTMLElementCollection * coll;
     IHTMLElement * element = NULL;
     IDispatch * pd = NULL;
     BSTR tagName;

     hr = doc->get_children(&pd);
     if FAILED(hr)
     {
         printf("GetChildElementByTagAttrAndAttr: get All: facility=%lx, severity=%lx, code=%lx\n", HRESULT_FACILITY(hr), HRESULT_SEVERITY(hr),HRESULT_CODE(hr));
         return NULL;
     }
     hr = pd->QueryInterface(IID_IHTMLElementCollection, (void **) &coll);
     if FAILED(hr) printf("GetChildElementByTagAttrAndAttr: QueryInterface: facility=%lx, severity=%lx, code=%lx\n", HRESULT_FACILITY(hr), HRESULT_SEVERITY(hr),HRESULT_CODE(hr));
     if (pd) pd->Release();

     hr = coll->get_length(&length);
     if FAILED(hr)
     {
       printf("GetChildElementByTagAttrAndAttr: get Length: facility=%lx, severity=%lx, code=%lx\n", HRESULT_FACILITY(hr), HRESULT_SEVERITY(hr),HRESULT_CODE(hr));
       return NULL;
     }

     while ((i < length) && (!foundTag))
     {
        foundTag = FALSE;

        VariantInit(&v1);
        v1.vt = VT_UINT;
        v1.lVal = i;
        hr = coll->item(v1, v2, &pd);
        if FAILED(hr) printf("GetChildElementByTagAttrAndAttr: collection item: facility=%lx, severity=%lx, code=%lx\n", HRESULT_FACILITY(hr), HRESULT_SEVERITY(hr),HRESULT_CODE(hr));
        VariantClear(&v1);

        hr = pd->QueryInterface(IID_IHTMLElement, (void **) &element);
        if FAILED(hr) printf("GetChildElementByTagAttrAndAttr: Element query interface: facility=%lx, severity=%lx, code=%lx\n", HRESULT_FACILITY(hr), HRESULT_SEVERITY(hr),HRESULT_CODE(hr));

        hr = element->get_tagName(&tagName);
        if FAILED(hr) printf("GetChildElementByTagAttrAndAttr: Get tagname: facility=%lx, severity=%lx, code=%lx\n", HRESULT_FACILITY(hr), HRESULT_SEVERITY(hr),HRESULT_CODE(hr));
        if (wcscmp(tagName, tag) ==0) foundTag = TRUE;
        SysFreeString(tagName);

        if (foundTag)
        {
           VariantInit(&r1);
           hr = element->getAttribute(attr1_name, 0, &r1);
           if FAILED(hr) printf("GetChildElementByTagAttrAndAttr: Get Attribute 1: facility=%lx, severity=%lx, code=%lx\n", HRESULT_FACILITY(hr), HRESULT_SEVERITY(hr),HRESULT_CODE(hr));
           if (wcscmp(attr1_value, r1.bstrVal) == 0)
           {
              if (attr2_name)
              {
                 VariantInit(&r2);
                 hr = element->getAttribute(attr2_name, 0, &r2);
                 if FAILED(hr) printf("GetChildElementByTagAttrAndAttr: Get Attribute 2: facility=%lx, severity=%lx, code=%lx\n", HRESULT_FACILITY(hr), HRESULT_SEVERITY(hr),HRESULT_CODE(hr));
                 if (wcscmp(attr2_value, r2.bstrVal) != 0)
                {
                   foundTag = FALSE;
                }
                SysFreeString(r2.bstrVal);
                VariantClear(&r2);
              }
           }
           else foundTag = FALSE;
           SysFreeString(r1.bstrVal);
           VariantClear(&r1);
         }
        i++;
        element->Release();
        if (!foundTag) LazyRelease(pd);
     }

     if (foundTag)
     {
        hr = pd->QueryInterface(IID_IHTMLElement, (void **) &element);
        if FAILED(hr) printf("GetChildElementByTagAttrAndAttr: Element query interface: facility=%lx, severity=%lx, code=%lx\n", HRESULT_FACILITY(hr), HRESULT_SEVERITY(hr),HRESULT_CODE(hr));
        pd->Release();
     }
     else element = NULL;

     coll->Release();
     return element;
  }


 IHTMLElement * GetChildElementByTagAndText(IHTMLElement * doc, wchar_t * tag, wchar_t * text)
  {
     HRESULT hr;
     VARIANT r, v1, v2;
     VariantInit(&v1);
     VariantInit(&v2);
     long length;
     long i = 0;
     int foundTag = 0;

     IHTMLElementCollection * coll;
     IHTMLElement * element = NULL;
     IDispatch * pd = NULL;
     BSTR tagname;

     hr = doc->get_children(&pd);
     if FAILED(hr)
     {
         printf("GetChildElementByTagAndText: get All: facility=%lx, severity=%lx, code=%lx\n", HRESULT_FACILITY(hr), HRESULT_SEVERITY(hr),HRESULT_CODE(hr));
         return NULL;
     }
     hr = pd->QueryInterface(IID_IHTMLElementCollection, (void **) &coll);
     if FAILED(hr) printf("GetChildElementByTagAndText: QueryInterface: facility=%lx, severity=%lx, code=%lx\n", HRESULT_FACILITY(hr), HRESULT_SEVERITY(hr),HRESULT_CODE(hr));
     if (pd) pd->Release();

     hr = coll->get_length(&length);
     if FAILED(hr)
     {
       printf("GetChildElementByTagAndText: get Length: facility=%lx, severity=%lx, code=%lx\n", HRESULT_FACILITY(hr), HRESULT_SEVERITY(hr),HRESULT_CODE(hr));
       return NULL;
     }

     while ((i < length) && (!foundTag))
     {
        foundTag = FALSE;

        VariantInit(&v1);
        v1.vt = VT_UINT;
        v1.lVal = i;
        hr = coll->item(v1, v2, &pd);
        if FAILED(hr) printf("GetChildElementByTagAndText: collection item: facility=%lx, severity=%lx, code=%lx\n", HRESULT_FACILITY(hr), HRESULT_SEVERITY(hr),HRESULT_CODE(hr));
        VariantClear(&v1);

        VariantInit(&r);
        hr = ole_invoke(pd, DISPATCH_PROPERTYGET, &r, NULL, NULL, L"tagName", NULL);
        if FAILED(hr) printf("GetChildElementByTagAndText: Get tagname: facility=%lx, severity=%lx, code=%lx\n", HRESULT_FACILITY(hr), HRESULT_SEVERITY(hr),HRESULT_CODE(hr));
        if (wcscmp(r.bstrVal, tag) ==0) foundTag = TRUE;
        SysFreeString(r.bstrVal);
        VariantClear(&r);

        if (foundTag)
        {
           VariantInit(&r);
           hr = ole_invoke(pd, DISPATCH_PROPERTYGET, &r, NULL, NULL, L"innerText", NULL);
           if FAILED(hr) printf("GetChildElementByTagAndText: Get tagname: facility=%lx, severity=%lx, code=%lx\n", HRESULT_FACILITY(hr), HRESULT_SEVERITY(hr),HRESULT_CODE(hr));
           if (r.bstrVal) {
             if (wcscmp(r.bstrVal, text) !=0) foundTag = FALSE;
             SysFreeString(r.bstrVal);
           }
           else foundTag = FALSE;
           VariantClear(&r);
         }
        i++;
        if (!foundTag) LazyRelease(pd);
     }

     if (foundTag)
     {
        hr = pd->QueryInterface(IID_IHTMLElement, (void **) &element);
        if FAILED(hr) printf("GetElemntByTagAndText: Element query interface: facility=%lx, severity=%lx, code=%lx\n", HRESULT_FACILITY(hr), HRESULT_SEVERITY(hr),HRESULT_CODE(hr));
        pd->Release();
     }

     coll->Release();
     return element;
  }


IHTMLElement * GetChildElementByTagAndPos(IHTMLElement * doc, wchar_t * tag, int pos)
   {
      HRESULT hr;
      VARIANT r, v1, v2;
      VariantInit(&v1);
      VariantInit(&v2);
      long length;
      long i = 0;
      int foundTag = 0;

      IHTMLElementCollection * coll;
      IHTMLElement * element = NULL;
      IDispatch * pd = NULL;
      BSTR tagname;

      hr = doc->get_children(&pd);
      if FAILED(hr)
      {
          printf("GetChildElementByTagAndText: get All: facility=%lx, severity=%lx, code=%lx\n", HRESULT_FACILITY(hr), HRESULT_SEVERITY(hr),HRESULT_CODE(hr));
          return NULL;
      }
      hr = pd->QueryInterface(IID_IHTMLElementCollection, (void **) &coll);
      if FAILED(hr) printf("GetChildElementByTagAndText: QueryInterface: facility=%lx, severity=%lx, code=%lx\n", HRESULT_FACILITY(hr), HRESULT_SEVERITY(hr),HRESULT_CODE(hr));
      if (pd) pd->Release();

      hr = coll->get_length(&length);
      if FAILED(hr)
      {
        printf("GetChildElementByTagAndText: get Length: facility=%lx, severity=%lx, code=%lx\n", HRESULT_FACILITY(hr), HRESULT_SEVERITY(hr),HRESULT_CODE(hr));
        return NULL;
      }

      while ((i < length) && (!foundTag))
      {
         foundTag = FALSE;

         VariantInit(&v1);
         v1.vt = VT_UINT;
         v1.lVal = i;
         hr = coll->item(v1, v2, &pd);
         if FAILED(hr) printf("GetChildElementByTagAndText: collection item: facility=%lx, severity=%lx, code=%lx\n", HRESULT_FACILITY(hr), HRESULT_SEVERITY(hr),HRESULT_CODE(hr));
         VariantClear(&v1);

         VariantInit(&r);
         hr = ole_invoke(pd, DISPATCH_PROPERTYGET, &r, NULL, NULL, L"tagName", NULL);
         if FAILED(hr) printf("GetChildElementByTagAndText: Get tagname: facility=%lx, severity=%lx, code=%lx\n", HRESULT_FACILITY(hr), HRESULT_SEVERITY(hr),HRESULT_CODE(hr));
         if (wcscmp(r.bstrVal, tag) ==0) foundTag = TRUE;
         SysFreeString(r.bstrVal);
         VariantClear(&r);

         if (foundTag)
         {
            pos--;
            if (pos != 0) foundTag = FALSE;
         }
         i++;
         if (!foundTag) LazyRelease(pd);
      }

      if (foundTag)
      {
         hr = pd->QueryInterface(IID_IHTMLElement, (void **) &element);
         if FAILED(hr) printf("GetElementByTagAndText: Element query interface: facility=%lx, severity=%lx, code=%lx\n", HRESULT_FACILITY(hr), HRESULT_SEVERITY(hr),HRESULT_CODE(hr));
         pd->Release();
      }

      coll->Release();
      return element;
   }


 extern "C" int browser_click_link(int o, int pos, wchar_t * text)
 {
   HWND ie = (HWND) o;

   IHTMLDocument2 * doc = get_IHTMLDocument2(ie);
   IHTMLElement * element;
   HRESULT hr;
   if (doc)
   {
      browser_wait_not_busy(doc);

      // Find and click the element
     if (pos > 0) element = GetElementByTagAttrAndPos(doc, L"A", NULL, NULL, pos);
     else element = GetElementByTagAndText(doc, L"A", text);
     if (element)
     {
        hr = element->click();
        if FAILED(hr)
        {
          printf("browser_click_link: element->click() failed: facility=%lx, severity=%lx, code=%lx\n", HRESULT_FACILITY(hr), HRESULT_SEVERITY(hr),HRESULT_CODE(hr));
          return -1;
        }
        element->Release();
     }
     else
    {
       wprintf(L"browser_click_link: link \"%s\" not found.\n", text);
       doc->Release();
       return -1;
    }
    doc->Release();
  }
  else
  {
       wprintf(L"browser_click_link: No HTML Document.\n");
       return -1;
  }
   return 0;
 }

 int window_click_button(HWND w, wchar_t * text)
 {
    HWND b = w;
    short id;
   BSTR unicodestr = 0;

   unicodestr = SysAllocString(text);

   // Convert String
   int a = SysStringLen(unicodestr)+1;
   char *ansistr = new char[a];
   WideCharToMultiByte(CP_ACP,  0, unicodestr, -1, ansistr, a, NULL, NULL);

    if (w)
   {
      WaitForWindow(w);
      LazySleep(500);

      b = FindControl(b, ansistr);
      if (b)
      {
          WaitForWindow(w);
          id = GetDlgCtrlID(b);
          SendMessageTimeout(GetParent(b), WM_COMMAND, MAKEWPARAM(id, BN_CLICKED), (LPARAM)b, SMTO_NORMAL, 1, NULL);
          LazySleep(1000);
      }
      else return -1;
    }

   delete[] ansistr;
   SysFreeString(unicodestr);
 }

 int window_enter_text(HWND w, wchar_t * text, int pos)
 {
    HWND b = w;
    short id;
   BSTR unicodestr = 0;
   int rv = -1;

   unicodestr = SysAllocString(text);

   // Convert String
   int a = SysStringLen(unicodestr)+1;
   char *ansistr = new char[a];
   WideCharToMultiByte(CP_ACP,  0, unicodestr, -1, ansistr, a, NULL, NULL);

    if (w)
   {
      WaitForWindow(w);
      LazySleep(500);

      b = FindEditControl(b, pos);
      if (b)
      {
          id = GetDlgCtrlID(b);
          SendMessageTimeout(b, WM_SETTEXT, a, (int) ansistr, SMTO_NORMAL, 1, NULL);
          rv = 0;
      }
    }

   delete[] ansistr;
   SysFreeString(unicodestr);

    return rv;
 }

 int window_entered_text(HWND w, int pos, char * buffer)
  {
     HWND b = w;
     short id;
     int rv = -1;

     if (w)
    {
       WaitForWindow(w);
       LazySleep(500);

       b = FindEditControl(b, pos);
       if (b)
       {
           id = GetDlgCtrlID(b);
           SendMessage(b, WM_GETTEXT, 32768, (int) buffer);
           rv = 0;
       }
     }

     return rv;
  }



  extern "C" int browser_click_button(int o, wchar_t * text)
 {
   HWND ie = (HWND) o;

   IHTMLDocument2 * doc = get_IHTMLDocument2(ie);
   IHTMLElement * element;
   HRESULT hr;

   if (doc)
   {
      browser_wait_not_busy(doc);

      // Find and click the element
     element = GetElementByTagAttrAndAttr(doc, L"INPUT", L"type", L"submit", L"value", text);
     if (element)
     {
        hr = element->click();
        if FAILED(hr)
        {
          printf("browser_click_link: element->click() failed: facility=%lx, severity=%lx, code=%lx\n", HRESULT_FACILITY(hr), HRESULT_SEVERITY(hr),HRESULT_CODE(hr));
          element->Release();
          return -1;
        }
        element->Release();
     }
     else
     {
    	doc->Release();
    	return window_click_button(ie, text);
     }
    doc->Release();
  }
  else return window_click_button(ie, text);
  return 0;
 }

  extern "C" int browser_set_checkbox(int o, bool state, int pos, wchar_t * id)
  {

    HWND ie = (HWND) o;

    IHTMLDocument2 * doc = get_IHTMLDocument2(ie);
    IHTMLElement * element;
    HRESULT hr;
    VARIANT r;

    if (doc)
    {
       browser_wait_not_busy(doc);

       // Find and click the element
      if (pos > 0) element = GetElementByTagAttrAndPos(doc, L"INPUT", L"TYPE", L"checkbox", pos);
      else
      {
          element = GetElementByTagAttrAndAttr(doc, L"INPUT", L"TYPE", L"checkbox", L"ID", id);
          if (!element) element = GetElementByTagAttrAndAttr(doc, L"INPUT", L"TYPE", L"checkbox", L"NAME", id);
      }
      if (element)
      {
         VariantInit(&r);
         r.vt = VT_BOOL;
         r.boolVal = state;
         hr = element->setAttribute(L"CHECKED", r, 0);
         if FAILED(hr) printf("browser_set_checkbox: set Value: facility=%lx, severity=%lx, code=%lx\n", HRESULT_FACILITY(hr), HRESULT_SEVERITY(hr),HRESULT_CODE(hr));
         VariantClear(&r);
         element->Release();
      }
     doc->Release();
   }
   return 0;
  }

  extern "C" int browser_get_checkbox(int o, int pos, wchar_t * id)
    {

      HWND ie = (HWND) o;

      IHTMLDocument2 * doc = get_IHTMLDocument2(ie);
      IHTMLElement * element;
      HRESULT hr;
      VARIANT r;
      bool state = FALSE;

      if (doc)
      {
         browser_wait_not_busy(doc);

         // Find and click the element
        if (pos > 0) element = GetElementByTagAttrAndPos(doc, L"INPUT", L"TYPE", L"checkbox", pos);
        else
        {
            element = GetElementByTagAttrAndAttr(doc, L"INPUT", L"TYPE", L"checkbox", L"ID", id);
            if (!element) element = GetElementByTagAttrAndAttr(doc, L"INPUT", L"TYPE", L"checkbox", L"NAME", id);
        }
        if (element)
        {
           VariantInit(&r);
           hr = element->getAttribute(L"CHECKED", 0, &r);
           if FAILED(hr) printf("browser_set_checkbox: set Value: facility=%lx, severity=%lx, code=%lx\n", HRESULT_FACILITY(hr), HRESULT_SEVERITY(hr),HRESULT_CODE(hr));
           state = r.boolVal;
           VariantClear(&r);
           element->Release();
        }
       doc->Release();
     }
     return state;
    }


  extern "C" int browser_set_radio(int o, bool state, int pos)
    {

      HWND ie = (HWND) o;

      IHTMLDocument2 * doc = get_IHTMLDocument2(ie);
      IHTMLElement * element;
      HRESULT hr;
      VARIANT r;

      if (doc)
      {
         browser_wait_not_busy(doc);

         // Find and click the element
        element = GetElementByTagAttrAndPos(doc, L"INPUT", L"TYPE", L"radio", pos);

        if (element)
        {
           VariantInit(&r);
           r.vt = VT_BOOL;
           r.boolVal = state;
           hr = element->setAttribute(L"CHECKED", r, 0);
           if FAILED(hr) printf("browser_set_checkbox: set Value: facility=%lx, severity=%lx, code=%lx\n", HRESULT_FACILITY(hr), HRESULT_SEVERITY(hr),HRESULT_CODE(hr));
           VariantClear(&r);
           element->Release();
        }
       doc->Release();
     }
     return 0;
    }

  extern "C" int browser_get_radio(int o, int pos)
    {

      HWND ie = (HWND) o;

      IHTMLDocument2 * doc = get_IHTMLDocument2(ie);
      IHTMLElement * element;
      HRESULT hr;
      VARIANT r;
      bool state = FALSE;

      if (doc)
      {
         browser_wait_not_busy(doc);

         // Find and click the element
        element = GetElementByTagAttrAndPos(doc, L"INPUT", L"TYPE", L"radio", pos);
        if (element)
        {
           VariantInit(&r);
           hr = element->getAttribute(L"CHECKED", 0, &r);
           if FAILED(hr) printf("browser_set_checkbox: set Value: facility=%lx, severity=%lx, code=%lx\n", HRESULT_FACILITY(hr), HRESULT_SEVERITY(hr),HRESULT_CODE(hr));
           state = r.boolVal;
           VariantClear(&r);
           element->Release();
        }
       doc->Release();
     }
     return state;
    }


 extern "C" int browser_enter_text(int o, wchar_t * text, int pos, wchar_t * id)
 {

   HWND ie = (HWND) o;

   IHTMLDocument2 * doc = get_IHTMLDocument2(ie);
   IHTMLElement * element;
   HRESULT hr;
   VARIANT r;

   if (doc)
   {
      browser_wait_not_busy(doc);

      // Find and click the element
     if (pos > 0) element = GetElementByTagAttrAndPos(doc, L"INPUT", L"TYPE", L"text|password", pos);
     else
     {
         element = GetElementByTagAttrAndAttr(doc, L"INPUT", L"TYPE", L"text|password", L"ID", id);
         if (!element) element = GetElementByTagAttrAndAttr(doc, L"INPUT", L"TYPE", L"text|password", L"NAME", id);
     }
     if (element)
     {
        VariantInit(&r);
        r.vt = VT_BSTR;
        r.bstrVal = SysAllocString(text);
        hr = element->setAttribute(L"VALUE", r, 0);
        if FAILED(hr) printf("browser_enter_text: set Value: facility=%lx, severity=%lx, code=%lx\n", HRESULT_FACILITY(hr), HRESULT_SEVERITY(hr),HRESULT_CODE(hr));
        SysFreeString(r.bstrVal);
        VariantClear(&r);
        element->Release();
     }
     else
     {
    	 doc->Release();
    	 return window_enter_text(ie, text, pos);
     }
    doc->Release();
  }
  else return window_enter_text(ie, text, pos);
  return 0;
 }

 extern "C" int browser_get_entered_text(int o, int pos, wchar_t * id, char * buffer )
  {

    HWND ie = (HWND) o;

    IHTMLDocument2 * doc = get_IHTMLDocument2(ie);
    IHTMLElement * element;
    HRESULT hr;
    VARIANT r;

    if (doc)
    {
       browser_wait_not_busy(doc);

       // Find and click the element
      if (pos > 0) element = GetElementByTagAttrAndPos(doc, L"INPUT", L"TYPE", L"text|password", pos);
      else
      {
          element = GetElementByTagAttrAndAttr(doc, L"INPUT", L"TYPE", L"text|password", L"ID", id);
          if (!element) element = GetElementByTagAttrAndAttr(doc, L"INPUT", L"TYPE", L"text|password", L"NAME", id);
      }
      if (element)
      {
         hr = element->getAttribute(L"VALUE", 0, &r);
         if FAILED(hr) printf("browser_enter_text: set Value: facility=%lx, severity=%lx, code=%lx\n", HRESULT_FACILITY(hr), HRESULT_SEVERITY(hr),HRESULT_CODE(hr));

         if (r.vt == VT_BSTR)
         {
        	if (r.bstrVal)
        	{
        	   int a = SysStringLen(r.bstrVal)+1;
        	   WideCharToMultiByte(CP_ACP,  0, r.bstrVal, -1, buffer, a, NULL, NULL);
        	   SysFreeString(r.bstrVal);
        	}
        	else strcpy(buffer, "");
         }
         else // Boolean
         {
        	if (r.boolVal) strcpy(buffer, "true");
        	else strcpy(buffer, "false");
         }
         VariantClear(&r);
         element->Release();
      }
      else
      {
    	  doc->Release();
    	  return window_entered_text(ie, pos, buffer);
      }
     doc->Release();
   }
   else return window_entered_text(ie, pos, buffer);
   return 0;
  }

 extern "C" int browser_select_item(int o, int item_pos, wchar_t * text, int pos, wchar_t * id)
 {

   HWND ie = (HWND) o;

   IHTMLDocument2 * doc = get_IHTMLDocument2(ie);
   IHTMLElement * element;
   HRESULT hr;
   VARIANT r;

   if (doc)
   {
      browser_wait_not_busy(doc);

      // Find and click the element
     if (pos > 0) element = GetElementByTagAttrAndPos(doc, L"SELECT", NULL, NULL, pos);
     else
     {
         element = GetElementByTagAttrAndAttr(doc, L"SELECT", L"ID", id, NULL, NULL);
         if (!element) element = GetElementByTagAttrAndAttr(doc, L"SELECT", L"NAME", id, NULL, NULL);
     }
     if (element)
     {
    	IHTMLElement * option;
    	if (item_pos == -1) option = GetChildElementByTagAndText(element, L"OPTION", text);
    	else option = GetChildElementByTagAndPos(element, L"OPTION", item_pos);
        if (option)
        {
    	   // Get option value
           VariantInit(&r);
           hr = option->getAttribute(L"VALUE", 0, &r);
           if FAILED(hr) printf("browser_select_item_text: get Value: facility=%lx, severity=%lx, code=%lx\n", HRESULT_FACILITY(hr), HRESULT_SEVERITY(hr),HRESULT_CODE(hr));
           // Set element value
           hr = element->setAttribute(L"VALUE", r, 0);
           if FAILED(hr) printf("browser_enter_text: set Value: facility=%lx, severity=%lx, code=%lx\n", HRESULT_FACILITY(hr), HRESULT_SEVERITY(hr),HRESULT_CODE(hr));
           SysFreeString(r.bstrVal);
           VariantClear(&r);
           option->Release();
        }
        element->Release();
     }
    doc->Release();
  }
  return 0;
 }

 extern "C" int browser_get_selected_item(int o, int pos, wchar_t * id, char *  buffer)
  {

    HWND ie = (HWND) o;

    IHTMLDocument2 * doc = get_IHTMLDocument2(ie);
    IHTMLElement * element;
    HRESULT hr;
    VARIANT r;

    if (doc)
    {
       browser_wait_not_busy(doc);

       // Find and click the element
      if (pos > 0) element = GetElementByTagAttrAndPos(doc, L"SELECT", NULL, NULL, pos);
      else
      {
          element = GetElementByTagAttrAndAttr(doc, L"SELECT", L"ID", id, NULL, NULL);
          if (!element) element = GetElementByTagAttrAndAttr(doc, L"SELECT", L"NAME", id, NULL, NULL);
      }
      if (element)
      {
    	VariantInit(&r);
    	hr = element->getAttribute(L"VALUE", 0, &r);
        if FAILED(hr) printf("browser_get_selected_item_text: get Value: facility=%lx, severity=%lx, code=%lx\n", HRESULT_FACILITY(hr), HRESULT_SEVERITY(hr),HRESULT_CODE(hr));
    	IHTMLElement * option = GetChildElementByTagAttrAndAttr(element, L"OPTION", L"VALUE", r.bstrVal, NULL, NULL);
  	    SysFreeString(r.bstrVal);
        VariantClear(&r);

        if (option)
        {
           // Get Inner Text from option
           BSTR txt;
           option->get_innerText(&txt);
           int a = SysStringLen(txt)+1;
           WideCharToMultiByte(CP_ACP,  0, txt, -1, buffer, a, NULL, NULL);
           SysFreeString(txt);
           option->Release();
         }
         element->Release();
      }
     doc->Release();
   }
   return 0;
  }

  extern "C" int browser_click_image(int o, int pos, wchar_t * alt, wchar_t * id)
 {

   HWND ie = (HWND) o;

   IHTMLDocument2 * doc = get_IHTMLDocument2(ie);
   IHTMLElement * element;
   HRESULT hr;
   VARIANT r;

   if (doc)
   {
      browser_wait_not_busy(doc);

      // Find and click the element
     if (pos > 0)  element = GetElementByTagAttrAndPos(doc, L"INPUT", L"TYPE", L"image", pos);
     else if (wcslen(alt) > 0)
     {
         element = GetElementByTagAttrAndAttr(doc, L"INPUT", L"TYPE", L"image", L"ALT", alt);
         if (!element) element = GetElementByTagAttrAndAttr(doc, L"IMG", L"ALT", alt, NULL, NULL);
     }
     else element = GetElementByTagAttrAndAttr(doc, L"INPUT", L"TYPE", L"image", L"ID", id);

     if (element)
     {
        hr = element->click();
        if FAILED(hr)
        {
          printf("browser_click_image: element->click() failed: facility=%lx, severity=%lx, code=%lx\n", HRESULT_FACILITY(hr), HRESULT_SEVERITY(hr),HRESULT_CODE(hr));
          return -1;
        }
        element->Release();
     }
     else return -1;
    doc->Release();
  }
  else return -1;
  return 0;
 }

  extern "C" int browser_get_html(int o, char * buffer)
 {

   HWND ie = (HWND) o;

   IHTMLDocument2 * doc = get_IHTMLDocument2(ie);
   IHTMLElement * body;
   IHTMLElement * html;
   BSTR src;
   int a = 0;

   if (doc)
   {
      browser_wait_not_busy(doc);
      doc->get_body(&body);
      body->get_parentElement(&html);
      html->get_outerHTML(&src);
      a = SysStringLen(src)+1;
      WideCharToMultiByte(CP_ACP,  0, src, -1, buffer, a, NULL, NULL);
      SysFreeString(src);
      body->Release();
      doc->Release();
   }

   return a;
 }


 int get_element_text(IHTMLElement * element, wchar_t * buffer)
{
   IHTMLElementCollection * children;
   IHTMLElement * next;
   IDispatch * pd;
   IDispatch * pd1;
   HRESULT hr;
   long length, i;
   VARIANT v1, v2;
   VariantInit(&v1);
   VariantInit(&v2);
   BSTR txt;
   BSTR tagName;
   int t = 0;

   hr = element->get_children(&pd);
   if FAILED(hr) printf("get_element_text: get_children: facility=%lx, severity=%lx, code=%lx\n", HRESULT_FACILITY(hr), HRESULT_SEVERITY(hr),HRESULT_CODE(hr));
   hr = pd->QueryInterface(IID_IHTMLElementCollection, (void **) &children);
   if FAILED(hr) printf("get_element_text: QueryInterface: facility=%lx, severity=%lx, code=%lx\n", HRESULT_FACILITY(hr), HRESULT_SEVERITY(hr),HRESULT_CODE(hr));
   pd->Release();
   hr = children->get_length(&length);
   if FAILED(hr) printf("get_element_text: get_length: facility=%lx, severity=%lx, code=%lx\n", HRESULT_FACILITY(hr), HRESULT_SEVERITY(hr),HRESULT_CODE(hr));
   if (length > 0)
   {
      i = 0;
      while (i < length)
      {
         VariantInit(&v1);
         v1.vt = VT_UINT;
         v1.lVal = i;
         hr = children->item(v1, v2, &pd1);
         if FAILED(hr) printf("get_element_text: collection item: facility=%lx, severity=%lx, code=%lx\n", HRESULT_FACILITY(hr), HRESULT_SEVERITY(hr),HRESULT_CODE(hr));
         VariantClear(&v1);
         hr = pd1->QueryInterface(IID_IHTMLElement, (void **) &next);
         if FAILED(hr) printf("get_element_text: Element query interface: facility=%lx, severity=%lx, code=%lx\n", HRESULT_FACILITY(hr), HRESULT_SEVERITY(hr),HRESULT_CODE(hr));
         hr = next->get_tagName(&tagName);
         if FAILED(hr) printf("get_element_text: Get tagname: facility=%lx, severity=%lx, code=%lx\n", HRESULT_FACILITY(hr), HRESULT_SEVERITY(hr),HRESULT_CODE(hr));
         if (wcsstr(L"DIV|A|B|I|SPAN|CODE|NOBR|H1|H2|H3|H4|TABLE|TBODY|TR|TH|TD|P|FONT|LEFT|CENTER|RIGHT|FORM", tagName) != NULL) t += get_element_text(next, buffer);
         SysFreeString(tagName);
         next->Release();
         LazyRelease(pd1);
         i++;
      }
   }
   else
   {
       element->get_innerText(&txt);
       if (txt != NULL)
       {
          wcscat(buffer, txt);
          wcscat(buffer, L"\n");
          SysFreeString(txt);
       }
       else
       {
          element->get_tagName(&tagName);
    	  if (wcscmp(tagName, L"TD")==0) wcscat(buffer, L"\n");
          SysFreeString(tagName);
       }
       t = 1;
     }
     children->Release();

  return t;
}


 extern "C" int browser_get_text(int o, char * buffer)
 {

   HWND ie = (HWND) o;

   IHTMLDocument2 * doc = get_IHTMLDocument2(ie);
   IHTMLElement * body;
   BSTR src;
   int a = 0;

   if (doc)
   {
      browser_wait_not_busy(doc);
      doc->get_body(&body);
      body->get_innerText(&src);
      a = SysStringLen(src)+1;
      WideCharToMultiByte(CP_ACP,  0, src, -1, buffer, a, NULL, NULL);
      SysFreeString(src);
      body->Release();
      doc->Release();
   }

   return a;
 }


 extern "C" int browser_get_tabulated_text(int o, char * buffer)
 {

   HWND ie = (HWND) o;

   IHTMLDocument2 * doc = get_IHTMLDocument2(ie);
   IHTMLElement * body;
   int a = 0;
   wchar_t * wbuff = (wchar_t *) malloc(1024 * 1024);
   wbuff[0] = 0;

   if (doc)
   {
      browser_wait_not_busy(doc);
      doc->get_body(&body);
      get_element_text(body, wbuff);
      a = wcslen(wbuff)+1;
      WideCharToMultiByte(CP_ACP,  0, wbuff, -1, buffer, a, NULL, NULL);
      body->Release();
      doc->Release();
   }
   free(wbuff);
   return a;
 }


 IHTMLElement * GetParentTable(IHTMLElement * start)
 {
   bool found = FALSE;
   IHTMLElement * element = start;
   IHTMLElement * parent;
   BSTR tag;
   element->AddRef();
   while ((element != NULL) && (!found)) {
	 element->get_parentElement(&parent);
	 element->Release();
	 element = NULL;
	 if (parent)
	 {
	   element = parent;
	   element->get_tagName(&tag);
	   if (tag)
	   {
		 found = (wcscmp(tag, L"TABLE") == 0);
		 SysFreeString(tag);
	   }
	 }
   }
   return element;
 }

 extern "C" int browser_get_tabulated_table_text(int o, wchar_t * text, char * buffer)
 {
   HWND ie = (HWND) o;

   IHTMLDocument2 * doc = get_IHTMLDocument2(ie);
   IHTMLElement * td;
   IHTMLElement * table;
   int a = 0;
   wchar_t * wbuff = (wchar_t *) malloc(1024 * 1024);
   wbuff[0] = 0;

   if (doc)
   {
      browser_wait_not_busy(doc);

      // Find element containing desired text
      td = GetElementByTagAndText(doc, L"TH", text);
      if (!td) td = GetElementByTagAndText(doc, L"TD", text);
      if (td)
      {
         table = GetParentTable(td);
         get_element_text(table, wbuff);
         a = wcslen(wbuff)+1;
         WideCharToMultiByte(CP_ACP,  0, wbuff, -1, buffer, a, NULL, NULL);
         td->Release();
      }
      doc->Release();
   }
   free(wbuff);
   return a;
 }

